
'''
K线相关公式

- 由于浮点数的精度问题，所以一般价格等金额都采用整数表示，根据 AMOUNT_FACTOR 系数进行转换
- 一般情况下，股票的 AMOUNT_FACTOR = 0.01 即价格等数值四舍五入精确到 分
- 滑动窗口函数的最小采样数都为 1，因此滑动窗口函数中一般没有 NAN 值
'''

__all__ = [
    'OPEN', 'HIGH', 'LOW', 'CLOSE', 'VOLUME', 'AMOUNT',
    'DATE', 'TIME', 'CODE', 'PERIOD_TYPE', 'PERIOD',
    'amount', 'AMOUNT_FACTOR', 'volume', 'VOLUME_FACTOR',
    'MA', 'MMAX', 'MMIN', 'REF',
    'logger',
]

from typing import TypeAlias, TypeVar, Generic, Callable, TYPE_CHECKING

if TYPE_CHECKING:
    from logging import Logger

logger:'Logger'

_T = TypeVar('_FT', float, int, str)
'''公式返回值类型'''
_NT = TypeVar('_NFT', float, int)
'''数值型公式的返回值类型'''

class Formula(Generic[_T]):
    ''''''

_F = TypeVar('_F', bound=Formula)
'''公式'''
_NF = TypeVar('_NF', bound=Formula[_NT])
'''数值型公式'''

Amount:TypeAlias = Formula[int]
'''金额, 单位根据 AMOUNT_FACTOR 系数而定'''

Volume:TypeAlias = Formula[float]
'''成交量'''

def amount(x: float|str) -> int:
    '''将以元为单位的金额根据 AMOUNT_FACTOR 系数转换为整数'''
    return round(x / AMOUNT_FACTOR)

def volume(x: float|str) -> float:
    '''将以股为单位的成交量根据 VOLUME_FACTOR 系数转换为浮点数
    '''
    return x * VOLUME_FACTOR

PERIOD_TYPE:str = None
'''周期类型，日、分钟'''
PERIOD:int = None
'''周期数，即当前是第几个周期, 
- 对于日线数据表示距股票上市开始的天数(交易日),
- 对分中线数据表示据开市(09:30)开始的分钟数
'''
AMOUNT_FACTOR:float = None
'''金额单位换算因子，开盘价乘以这个系数后才能换算成以元为单位的数值''
- 一般取值为 0.01, 即价格等金额精确到 分
'''
VOLUME_FACTOR:float = None
'''成交量单位换算因子，成交量乘以这个系数才和 VOLUME 的单位一致
'''

CODE:str  = None
'''证券代码，例如 sz000001'''
DATE:Formula[str] = None
'''yyyy-mm-dd 格式的日期'''
TIME:Formula[str] = None
'''HH:MM:SS 格式的时间，日线数据时无效'''

OPEN:Amount = None
'''开盘价'''
HIGH:Amount = None
'''最高价'''
LOW:Amount = None
'''最低价'''
CLOSE:Amount = None
'''收盘价'''
VOLUME:Volume = None
'''成交量'''
AMOUNT:Amount = None
'''成交额'''

def REF(x:_F, n:int) -> _F: 
    '''n 个周期前的值，例如日线的 REF(CLOSE, 1) 表示前一天的收盘价'''
    return _get_data('REF', x, n)

REF1: Callable[[_F], _F] = lambda x: REF(x, 1) 
'''REF(x, 1) 的简写'''

def MMAX(x:_NF, n:int) -> _NF:
    '''n 个周期的最大值'''
    return _get_data('MMIN', x, n)

def MMIN(x:_NF, n:int) -> _NF:
    '''n 个周期的最小值'''
    return _get_data('MMIN', x, n)

def MA(x:_NF, n:int) -> _NF:
    '''n 个周期的移动平均值'''
    return _get_data('MA', x, n)

MA5:Callable[[_NF], _NF] = lambda x: MA(x, 5)
'''MA(x, 5) 的简写'''

import random
from typing import Any, Literal

_datas:dict[str, Any] = None

# 这里我们利用 python 一切都是对象的特性，由于值也是对象，所以可以根据值找出其对应的变量名
def _get_data(f, x, n):
    def get_name(x):
        for k, v in _datas.items():
            if v is x:    
                return k
        raise Exception('x is not found in _datas')
    if _datas is None:
        return random.randint(1, 100)
    xname = get_name(x)
    name = f'{f}({xname},{n})'
    return _datas[name]

